<script type="typescript">
import { page } from '$app/stores';
import { request } from '../../../../../stores/auth';
import Spinner from '../../../../../components/Spinner.svelte';
import SvelteMarkdown from 'svelte-markdown';
import Icon from '../../../../../components/Icon.svelte';
import type { Crate } from '../../../../../types/crate';
import Dependency from './Dependency.svelte';
import VersionTab from './VersionTab.svelte';
import MemberTab from './MemberTab.svelte';
import RegistryDefinition from './RegistryDefinition.svelte';
import DependencyDefinition from './DependencyDefinition.svelte';
let cratePromise: Promise<Crate>;
$: cratePromise = request(`/web/v1/crates/${$page.params.organisation}/${$page.params.crate}`);
* Contains all the possible tabs, used for maintaining state on the current tab.
*/
enum Tab {
README,
VERSIONS,
MEMBERS,
}
* Mapping of `Tab`s to their human-readable form alongside a friendly icon to show to the
* user.
*/
let allTabs = [
{
id: Tab.README,
name: 'Readme',
icon: 'book-open',
},
{
id: Tab.VERSIONS,
name: 'Versions',
icon: 'archive',
},
];
let currentTab = Tab.README;
$: cratePromise.then(filterTabsForCrate);
* Filters the tabs displayed to the user depending on their current permissions for the
* crate.
*/
function filterTabsForCrate(crate: Crate) {
if (crate.permissions.includes('MANAGE_USERS')) {
if (!allTabs.some((tab) => tab.id === Tab.MEMBERS)) {
allTabs = [
...allTabs,
{
id: Tab.MEMBERS,
name: 'Members',
icon: 'user',
},
];
}
} else {
allTabs = allTabs.filter((tab) => tab.id !== Tab.MEMBERS);
if (currentTab === Tab.MEMBERS) {
currentTab = Tab.README;
}
}
}
</script>
<header>
<div class="container flex mx-auto p-10 mb-3">
<div class="flex-grow">
<h1 class="text-5xl font-bold tracking-tight">
<a href={`/crates/${$page.params.organisation}`}>{$page.params.organisation}</a>/<span
class="text-highlight">{$page.params.crate}</span
>
</h1>
{#await cratePromise}
<div class="h-6">
<div class="skeleton inline-block mr-2 w-12" />
<div class="skeleton inline-block mr-2 w-24" />
<div class="skeleton inline-block mr-2 w-16" />
<div class="skeleton inline-block mr-2 w-32" />
<div class="skeleton inline-block mr-2 w-12" />
<div class="skeleton inline-block w-10" />
</div>
<div class="space-x-2">
<div class="card-header-button btn-skeleton-outline">
<div class="skeleton inline-block w-10" />
</div>
<div class="card-header-button btn-skeleton-outline">
<div class="skeleton inline-block w-10" />
</div>
<div class="card-header-button btn-skeleton-outline">
<div class="skeleton inline-block w-10" />
</div>
</div>
{:then crate}
<h2>
{#if crate.description}
{crate.description}
{:else}
<em>No description given.</em>
{/if}
</h2>
<div class="space-x-2">
{#if crate.homepage}
<a href={crate.homepage} target="_blank" class="card-header-button btn-blue-outline">
<Icon name="home" />
Home
</a>
{/if}
{#if crate.repository}
<a href={crate.repository} target="_blank" class="card-header-button btn-blue-outline">
<Icon name="git-branch" />
Repo
</a>
{/if}
{#if crate.documentation}
<a href={crate.documentation} target="_blank" class="card-header-button btn-blue-outline">
<Icon name="book" />
Docs
</a>
{/if}
</div>
{/await}
</div>
</div>
</header>
<main class="container mx-auto p-10 pt-0 grid grid-cols-12 gap-6 relative">
<div class="card col-span-full lg:col-span-9 p-0">
<div class="border-b border-gray-200 dark:border-gray-700">
<ul class="flex flex-wrap -mb-px text-sm font-medium text-center text-gray-500 dark:text-gray-400">
{#each allTabs as tab}
<li class="mr-2">
<button
on:click={() => (currentTab = tab.id)}
class:!border-blue-500={currentTab === tab.id}
class:text-blue-500={currentTab === tab.id}
aria-current={currentTab === tab.id ? 'page' : false}
class="inline-flex items-center space-x-2 p-4 rounded-t-lg border-b-2 border-transparent"
>
<Icon name={tab.icon} /> <span>{tab.name}</span>
</button>
</li>
{/each}
</ul>
</div>
{#if currentTab === Tab.README}
<article
class="mt-8 px-6 pb-6 prose dark:prose-invert text-inherit max-w-full prose-headings:text-inherit hover:prose-a:text-blue-600 prose-a:text-blue-500 prose-code:p-1 prose-code:bg-slate-100 dark:prose-code:bg-slate-700 prose-code:rounded-lg prose-pre:bg-slate-100 dark:prose-pre:bg-slate-700 leading-6 before:prose-code:content-none after:prose-code:content-none prose-code:text-pink-400 prose-code:font-normal prose-strong:text-inherit prose-img:inline prose-img:my-0"
>
{#await cratePromise}
<div class="skeleton inline-block mr-2 w-24" />
<br /><br />
{#each [...Array(200).keys()] as _}
<div
style={`width: ${Math.max(Math.random() * 24, 4)}rem`}
class={`skeleton inline-block mr-2`}
/>
{/each}
{:then crate}
{#if crate.readme}
<SvelteMarkdown source={crate.readme} />
{:else}
<em>No README exists for the current crate version.</em>
{/if}
{/await}
</article>
{:else if currentTab === Tab.MEMBERS}
<MemberTab />
{:else if currentTab === Tab.VERSIONS}
<div class="divide-y divide-gray-200 dark:divide-gray-700">
{#await cratePromise}
<div class="relative h-20"><Spinner /></div>
{:then crate}
{#each crate.versions as version}
<VersionTab {version} class="p-6" />
{/each}
{/await}
</div>
{/if}
</div>
<div class="col-span-full lg:col-span-3">
{#if import.meta.env.VITE_CHARTERED_SSH_URL}
<div class="card p-0 mb-6">
<h1 class="text-xl p-3 border-b border-gray-200 dark:border-gray-700 font-medium">Get Started</h1>
<div class="divide-y divide-gray-200 dark:divide-gray-700">
<div class="p-3 pb-0">
<strong class="text-xs pointer-events-none select-none">.cargo/config.toml</strong>
<div class="overflow-scroll pb-3">
<pre><code><RegistryDefinition /></code></pre>
</div>
</div>
<div class="p-3 pb-0">
<strong class="text-xs pointer-events-none select-none">Cargo.toml</strong>
<div class="overflow-scroll pb-3">
<pre><code><DependencyDefinition {cratePromise} /></code></pre>
</div>
</div>
</div>
</div>
{/if}
<div class="card p-0">
<h1 class="text-xl p-3 border-b border-gray-200 dark:border-gray-700 font-medium">Dependencies</h1>
<div class="divide-y divide-gray-200 dark:divide-gray-700">
{#await cratePromise}
{#each [...Array(Math.floor(Math.random() * 8 + 5)).keys()] as _}
<div class="flex items-center py-5">
<div
style={`width: ${Math.max(Math.random() * 12, 4)}rem`}
class={`skeleton inline-block ml-3 mr-2`}
/>
<div class={`skeleton inline-block mr-3 w-12`} />
</div>
{/each}
{:then crate}
{#each crate.versions[0].deps as dependency}
<Dependency {dependency} class="p-3" />
{/each}
{/await}
</div>
</div>
</div>
</main>
<style lang="postcss">
.card-header-button {
@apply inline-flex items-center gap-x-1;
}
</style>